#pragma once

#include "obj.h"
#include "logger.h"
#include <string.h>

OPEN_JLIB_NS


struct Buffer : public Obj {

  inline Buffer (const Buffer& other) noexcept : _size(other._size) {
    if (other._data) {
      _data = (u8*)malloc(_size);
      memcpy(_data, other._data, _size);
    }
    else _data = nullptr;
  }

  inline Buffer (Buffer&& other) noexcept : _data(other._data), _size(other._size) {
    if (other._data) {
      free(other._data);
      other._data = nullptr;
    }
  }

  inline Buffer () noexcept : _data(nullptr), _size(0) {
  }

  inline Buffer (void *data, uwl capacity) noexcept : _data((u8*)data), _size(capacity) {
  }

  inline explicit Buffer (uwl capacity) noexcept {
    _data = (u8*)malloc(_size = capacity);
  }

  ~Buffer () override {
    if (_data)
      free(_data);
  }

  inline operator bool () const noexcept {
    return _data != nullptr;
  }

  Buffer& resize (uwl newSize) noexcept {
    if (!newSize) {
      if (_data)
        free(_data);
      _data = nullptr;
      _size = 0;
    }
    else {
      if (_data)
        _data = (u8*)realloc(_data, _size = newSize);
      else
        _data = (u8*)malloc(_size = newSize);
    }
    return *this;
  }


  inline uwl size () const noexcept {
    debug_assert(_data);
    return _size;
  }

  inline const u8* data () const noexcept {
    debug_assert(_data);
    return _data;
  }

  inline u8* data () noexcept {
    debug_assert(_data);
    return _data;
  }

  inline u8 read (uwl index) const noexcept {
    debug_assert(_data);
    return _data[index];
  }

  inline uwl read (void* buf, uwl start, uwl size) const noexcept {
    debug_assert(_data);
    if (start >= _size)
      return 0;
    auto rsize = _size - start;
    auto rread = rsize < size ? rsize : size;
    memcpy(buf, _data + start, rread);
    return rread;
  }

  inline void write (uwl index, u8 value) noexcept {
    _data[index] = value;
  }

  inline void write (const void* buf, uwl start, uwl size) noexcept {
    debug_assert(_data);
    auto rcapacity = start + size;
    if (rcapacity > _size)
      _data = (u8*)realloc(_data, _size = rcapacity);
    memcpy(_data + start, buf, size);
  }


public:
  u8* _data;
  uwl _size;
};


CLOSE_JLIB_NS